package utils

import "sync"

type ObjectKeeper[T interface{}] struct {
	object T
	locker sync.RWMutex
}

func (this *ObjectKeeper[T]) Get() T {
	this.locker.RLock()
	defer this.locker.RUnlock()
	return this.object
}

func (this *ObjectKeeper[T]) Set(newValue T) {
	this.locker.Lock()
	defer this.locker.Unlock()
	this.object = newValue
}

func (this *ObjectKeeper[T]) ComputeAndGet(valueComputer func(oldValue T) T) T {
	this.locker.Lock()
	defer this.locker.Unlock()
	newValue := valueComputer(this.object)
	this.object = newValue
	return newValue
}

func (this *ObjectKeeper[T]) SetWhen(
	conditionFunc func(currentValue T) bool,
	newValue T,
) (needUpdate bool, currentValue T) {
	this.locker.Lock()
	defer this.locker.Unlock()
	needUpdate = conditionFunc(this.object)
	if needUpdate {
		this.object = newValue
	}
	return needUpdate, this.object
}

func (this *ObjectKeeper[T]) ComputeWhen(
	conditionFunc func(currentValue T) bool,
	valueComputer func(oldValue T) T,
) (needUpdate bool, currentValue T) {
	this.locker.Lock()
	defer this.locker.Unlock()
	needUpdate = conditionFunc(this.object)
	if needUpdate {
		currentValue = valueComputer(this.object)
		this.object = currentValue
	}
	return needUpdate, this.object
}
